home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Disc to the Future 2
/
Disc to the Future Part II Programmer's Reference (Wayzata Technology)(6013)(1992).bin
/
MAC
/
MPW_TOOL
/
TOOLS
/
TOOLS_WI
/
ICON_8
/
ICONX_FO
/
RSTRUCT.C
< prev
next >
Wrap
Text File
|
1990-03-02
|
15KB
|
467 lines
/*
* File: rstruct.c
* Contents: addmem, cplist, cpset, hmake, hchain, hgener, hgrow, hshrink, memb
*/
#include "::h:config.h"
#include "::h:rt.h"
#include "rproto.h"
/*
* addmem - add a new set element block in the correct spot in
* the bucket chain.
*/
novalue addmem(ps,pe,pl)
union block **pl;
struct b_set *ps;
struct b_selem *pe;
{
ps->size++;
if (*pl != NULL )
pe->clink = *pl;
*pl = (union block *) pe;
}
/*
* cplist(dp1,dp2,i,j) - copy sublist dp1[i:j] into dp2.
*/
int cplist(dp1, dp2, i, j)
dptr dp1, dp2;
word i, j;
{
register dptr dp;
word size, nslots;
struct b_list *lp1, *lp2;
struct b_lelem *bp1, *bp2;
/*
* Calculate the size of the sublist and fail if it's less than 0.
* Also round nslots up to the minimum list block size.
*/
size = nslots = j - i;
/*
* Get pointers to the list and list elements for the source list
* (bp1, lp1) and the sublist (bp2, lp2).
*/
if (blkreq(sizeof(struct b_list) + sizeof(struct b_lelem) +
nslots * sizeof(struct descrip)) == Error)
return Error;
lp1 = (struct b_list *) BlkLoc(*dp1);
bp1 = (struct b_lelem *) lp1->listhead;
lp2 = (struct b_list *) alclist(size);
bp2 = (struct b_lelem *) alclstb(nslots, (word)0, size);
lp2->listhead = lp2->listtail = (union block *) bp2;
dp = bp2->lslots;
/*
* Locate the block containing element i in the source list.
*/
if (size > 0) {
while (i > bp1->nused) {
i -= bp1->nused;
bp1 = (struct b_lelem *) bp1->listnext;
}
}
/*
* Copy elements from the source list into the sublist, moving to
* the next list block in the source list when all elements in a
* block have been copied.
*/
while (size > 0) {
j = bp1->first + i - 1;
if (j >= bp1->nslots)
j -= bp1->nslots;
*dp++ = bp1->lslots[j];
if (++i > bp1->nused) {
i = 1;
bp1 = (struct b_lelem *) bp1->listnext;
}
size--;
}
/*
* Fix type and location fields for the new list.
*/
dp2->dword = D_List;
BlkLoc(*dp2) = (union block *) lp2;
return Success;
}
/*
* cpset(dp1,dp2,n) - copy set dp1 to dp2, reserving memory for n entries.
*/
int cpset(dp1, dp2, n)
dptr dp1, dp2;
word n;
{
register union block **tp, *ep, *old, *new;
register struct b_slots *seg;
register word i, slotnum;
/*
* Make a new set organized like dp1, with room for n elements.
*/
new = hmake(T_Set, BlkLoc(*dp1)->set.mask + 1, n);
if (new == NULL)
return Error;
/*
* Copy the header and slot blocks.
*/
old = BlkLoc(*dp1);
new->set.size = old->set.size; /* actual set size */
new->set.mask = old->set.mask; /* hash mask */
for (i = 0; i < HSegs && old->set.hdir[i] != NULL; i++)
memcopy((char *)new->set.hdir[i], (char *)old->set.hdir[i],
old->set.hdir[i]->blksize);
/*
* Work down the chain of element blocks in each bucket
* and create identical chains in new set.
*/
for (i = 0; i < HSegs && (seg = new->set.hdir[i]) != NULL; i++)
for (slotnum = segsize[i] - 1; slotnum >= 0; slotnum--) {
tp = &seg->hslots[slotnum];
for (ep = *tp; ep != NULL; ep = *tp) {
*tp = (union block *)alcselem(&ep->selem.setmem, ep->selem.hashnum);
(*tp)->selem.clink = ep->selem.clink;
tp = &(*tp)->selem.clink;
}
}
dp2->dword = D_Set;
BlkLoc(*dp2) = new;
if (TooSparse(new))
hshrink(dp2);
return Success;
}
/*
* hmake - make a hash structure (Set or Table) with a given number of slots.
* hmake also ensures adequate storage for *nelem* elements, but does not
* allocate then. If *nslots* is zero, a value appropriate for *nelem*
* elements is chosen.
*/
union block *hmake(tcode, nslots, nelem)
int tcode;
word nslots, nelem;
{
word seg, t, nbytes, blksize, elemsize;
union block *blk;
if (nslots == 0)
nslots = (nelem + MaxHLoad - 1) / MaxHLoad;
for (seg = t = 0; seg < (HSegs - 1) && (t += segsize[seg]) < nslots; seg++)
;
nslots = ((word)HSlots) << seg; /* ensure legal power of 2 */
if (tcode == T_Table) {
blksize = sizeof(struct b_table);
elemsize = sizeof(struct b_telem);
}
else { /* T_Set */
blksize = sizeof(struct b_set);
elemsize = sizeof(struct b_selem);
}
nbytes = blksize + (seg + 1) * (sizeof(struct b_slots) - (HSlots*WordSize)) +
nslots * WordSize + nelem * elemsize;
if (blkreq(nbytes) == Error)
return NULL; /* sorry, no memory */
blk = alchash(tcode);
for (; seg >= 0; seg--)
blk->set.hdir[seg] = alcsegment(segsize[seg]);
blk->set.mask = nslots - 1;
return blk;
}
/*
* hchain - return a pointer to the word that points to the head of the hash
* chain for hash number hn in hashed structure s.
*/
/*
* lookup table for log to base 2; must have powers of 2 through (HSegs-1)/2.
*/
static unsigned char log2[] = {
0,1,2,2, 3,3,3,3, 4,4,4,4, 4,4,4,4, 5,5,5,5, 5,5,5,5, 5,5,5,5, 5,5,5,5,
};
union block **hchain(pb, hn)
union block *pb;
register uword hn;
{
register struct b_set *ps;
register word slotnum, segnum, segslot;
ps = (struct b_set *)pb;
slotnum = hn & ps->mask;
if (slotnum >= HSlots * sizeof(log2))
segnum = log2[slotnum >> (LogHSlots + HSegs/2)] + HSegs/2;
else
segnum = log2[slotnum >> LogHSlots];
segslot = hn & (segsize[segnum] - 1);
return &ps->hdir[segnum]->hslots[segslot];
}
/*
* hgener - agent function to generate the elements of a hashed structure.
*
* Arg1 = set or table to enumerate
* Arg2 = integer value indicating desired action:
* 0 generate set elements
* 1 generate table keys
* 2 generate table values
*
* Carefully generate each element exactly once, even if the hash chains
* split while suspended. Do this by recording the state of things at the
* time of the split and checking past history when starting to process a
* new chain.
*
* Elements inserted or deleted while the generator is suspended may or
* may not be generated.
*
* We assume that no structure *shrinks* after its initial creation; they
* only *grow*.
*/
AgtDcl(hgener)
{
int i, segnum;
word d, m, func, slotnum;
uword hn;
union block *ep;
word tmask; /* structure mask before suspension */
word sgmask[HSegs]; /* mask being used when the segment was created */
uword sghash[HSegs]; /* hashnum in process when the segment was created */
for (i = 0; i < HSegs; i++)
sghash[i] = sgmask[i] = 0; /* set initial state */
tmask = BlkLoc(Arg1)->table.mask;
func = IntVal(Arg2); /* save function code */
Arg2.dword = D_Telem; /* use Arg2 to tend address */
for (segnum = 0; segnum < HSegs; segnum++) {
if (BlkLoc(Arg1)->table.hdir[segnum] == NULL)
break;
for (slotnum = 0; slotnum < segsize[segnum]; slotnum++) {
ep = BlkLoc(Arg1)->table.hdir[segnum]->hslots[slotnum];
/*
* Check to see if parts of this hash chain were already processed.
* This could happen if the elements were in a different chain,
* but a split occurred while we were suspended.
*/
for (i = segnum; (m = sgmask[i]) != 0; i--) {
d = (word)(m & slotnum) - (word)(m & sghash[i]);
if (d < 0) /* if all elements processed earlier */
ep = NULL; /* skip this slot */
else if (d == 0) {
/*
* This chain was split from its parent while the parent was
* being processed. Skip past elements already processed.
*/
while (ep != NULL && ep->telem.hashnum <= sghash[i])
ep = ep->telem.clink;
}
}
/*
* Process the elements of the hash chain, in turn.
*/
while (ep != NULL) {
switch ((int)func) {
case 0: Arg0 = ep->selem.setmem; break;
case 1: Arg0 = ep->telem.tref; break;
case 2: Arg0 = ep->telem.tval; break;
}
BlkLoc(Arg2) = ep; /* save pointer, so it gets tended */
Suspend; /* suspend, returning current element */
ep = BlkLoc(Arg2); /* restore pointer */
if (BlkLoc(Arg1)->table.mask != tmask &&
(ep->telem.clink == NULL ||
ep->telem.clink->telem.hashnum != ep->telem.hashnum)) {
/*
* The set or table's hash buckets split, once or more, while
* we were suspended. (We notice this unless the next entry
* has same hash value as the current one. In that case we
* ignore it for now and will pick it up on the next pass.)
*
* Make a note of the current state.
*/
hn = ep->telem.hashnum;
for (i = 1; i < HSegs; i++)
if ((((word)HSlots) << (i - 1)) > tmask) {
/*
* For the newly created segments only, save the mask and
* hash number being processed at time of creation.
*/
sgmask[i] = tmask;
sghash[i] = hn;
}
tmask = BlkLoc(Arg1)->table.mask;
/*
* Find the next element in our original segment by starting
* from the beginning and skipping through the current hash
* number. We can't just follow the link from the current
* element, because it may have moved to a new segment.
*/
ep = BlkLoc(Arg1)->table.hdir[segnum]->hslots[slotnum];
while (ep != NULL && ep->telem.hashnum <= hn)
ep = ep->telem.clink;
}
else {
/*
* Nothing happened during the suspend, or else if it did we're
* between items with identical hash numbers. Just move on.
*/
ep = ep->telem.clink;
}
}
}
}
Fail;
}
/*
* hgrow - split a hashed structure (doubling the buckets) for faster access.
*/
novalue hgrow(dp)
dptr dp;
{
register union block **tp0, **tp1, *ep;
register word newslots, slotnum, segnum;
struct b_set *ps;
struct b_slots *seg, *newseg;
union block **curslot;
ps = (struct b_set *)BlkLoc(*dp);
if (ps->hdir[HSegs-1] != NULL)
return; /* can't split further */
newslots = ps->mask + 1;
if (blkreq(sizeof(struct b_slots) + (newslots - HSlots) * WordSize) == Error)
return; /* sorry, no memory */
ps = (struct b_set *)BlkLoc(*dp); /* refresh address -- may have moved */
newseg = alcsegment(newslots);
curslot = newseg->hslots;
for (segnum = 0; (seg = ps->hdir[segnum]) != NULL; segnum++)
for (slotnum = 0; slotnum < segsize[segnum]; slotnum++) {
tp0 = &seg->hslots[slotnum]; /* ptr to tail of old slot */
tp1 = curslot++; /* ptr to tail of new slot */
for (ep = *tp0; ep != NULL; ep = ep->selem.clink) {
if ((ep->selem.hashnum & newslots) == 0) {
*tp0 = ep; /* element does not move */
tp0 = &ep->selem.clink;
}
else {
*tp1 = ep; /* element moves to new slot */
tp1 = &ep->selem.clink;
}
}
*tp0 = *tp1 = NULL;
}
ps->hdir[segnum] = newseg;
ps->mask = (ps->mask << 1) | 1;
}
/*
* hshrink - combine buckets in a set or table that is too sparse.
*
* Call this only for newly created structures. Shrinking an active structure
* can wreak havoc on suspended generators.
*/
novalue hshrink(dp)
dptr dp;
{
register union block **tp, *ep0, *ep1;
int topseg, curseg;
word slotnum;
struct b_set *ps;
struct b_slots *seg;
union block **uppslot;
ps = (struct b_set *)BlkLoc(*dp);
topseg = 0;
for (topseg = 1; topseg < HSegs && ps->hdir[topseg] != NULL; topseg++)
;
topseg--;
while (TooSparse(ps)) {
uppslot = ps->hdir[topseg]->hslots;
ps->hdir[topseg--] = NULL;
for (curseg = 0; (seg = ps->hdir[curseg]) != NULL; curseg++)
for (slotnum = 0; slotnum < segsize[curseg]; slotnum++) {
tp = &seg->hslots[slotnum]; /* tail pointer */
ep0 = seg->hslots[slotnum]; /* lower slot entry pointer */
ep1 = *uppslot++; /* upper slot entry pointer */
while (ep0 != NULL && ep1 != NULL)
if (ep0->selem.hashnum < ep1->selem.hashnum) {
*tp = ep0;
tp = &ep0->selem.clink;
ep0 = ep0->selem.clink;
}
else {
*tp = ep1;
tp = &ep1->selem.clink;
ep1 = ep1->selem.clink;
}
while (ep0 != NULL) {
*tp = ep0;
tp = &ep0->selem.clink;
ep0 = ep0->selem.clink;
}
while (ep1 != NULL) {
*tp = ep1;
tp = &ep1->selem.clink;
ep1 = ep1->selem.clink;
}
}
ps->mask >>= 1;
}
}
/*
* memb - sets res flag to 1 if x is a member of a set or table, or to 0 if not.
* Returns a pointer to the word which points to the element, or which
* would point to it if it were there.
*/
union block **memb(pb, x, hn, res)
union block *pb;
dptr x;
register uword hn;
int *res; /* pointer to integer result flag */
{
struct b_set *ps;
register union block **lp;
register struct b_selem *pe;
register uword eh;
ps = (struct b_set *)pb;
lp = hchain(pb, hn);
/*
* Look for x in the hash chain.
*/
*res = 0;
while ((pe = (struct b_selem *)*lp) != NULL) {
eh = pe->hashnum;
if (eh > hn) /* too far - it isn't there */
return lp;
else if ((eh == hn) && (equiv(&pe->setmem, x))) {
*res = 1;
return lp;
}
/*
* We haven't reached the right hashnumber yet or
* the element isn't the right one so keep looking.
*/
lp = &(pe->clink);
}
/*
* At end of chain - not there.
*/
return lp;
}